package com.ndp.fb.mq.service.schedulerservice.adpublish;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ndp.ec.core.api.DPAAdPerformService;
import com.ndp.fb.enums.publish.PublishResultType;
import com.ndp.fb.enums.publish.PublishStatus;
import com.ndp.fb.exception.CheckedException;
import com.ndp.fb.exception.ErrorCode;
import com.ndp.fb.fbagent.service.AdGroupFBService;
import com.ndp.fb.model.fbagent.param.APIPriority;
import com.ndp.fb.model.fbagent.param.ApiCallBackTarget;
import com.ndp.fb.model.fbagent.param.adgroup.AdGroupParam;
import com.ndp.fb.model.fbagent.param.adgroup.TrackingSpecs;
import com.ndp.fb.model.fbagent.result.ReDownloadObject;
import com.ndp.fb.mongo.api.PublishCacheMongoService;
import com.ndp.fb.mq.api.service.publishhandleservice.PublishPostProcessorHandle;
import com.ndp.fb.mq.api.service.publishhandleservice.PublishPostResult;
import com.ndp.fb.rdb.api.FbUserRdbService;
import com.ndp.fb.rdb.api.PublishBatchRdbService;
import com.ndp.fb.rdb.api.PublishResultDetailRdbService;
import com.ndp.fb.rdb.api.PublishResultRecordRdbService;
import com.ndp.fb.rdb.model.BaseAdLevel;
import com.ndp.fb.rdb.model.PublishBatch;
import com.ndp.fb.redis.model.AdCreativeLevel;
import com.ndp.fb.redis.model.AdGroupLevel;
import com.ndp.fb.redis.model.AdSetLevel;
import com.ndp.fb.util.ThreadPool;

/**
 * Created by Jacky on 2015/3/12.
 */
@Component
public class PublishAdGroupService extends  PublishBase {
    public static final String AD_GROUP = "adGroup";
    private static Logger logger = LoggerFactory.getLogger(PublishAdGroupService.class);

    @Reference
    private PublishResultRecordRdbService publishResultRecordRdbService;

    @Reference
    private PublishResultDetailRdbService publishResultDetailRdbService;

    @Reference
    private AdGroupFBService adGroupFBService;


    @Reference
    private PublishBatchRdbService publishBatchRdbService;

    @Reference
    private FbUserRdbService fbUserRdbService;

    @Autowired
    private PublishCacheMongoServiceFailover publishCacheMongoService;

    @Autowired
    private PublishPostProcessorHandle publishPostProcessorHandle;

    @Override
    public void publish(Long publishBatchId, String adSetBatchIndex, BaseAdLevel baseAdLevel) {
        logger.info("PublishAdGroupJob publishAdGroups 调用开始 ");
        logger.info("PublishAdGroupJob publishAdGroups publishBatchId >>>> " + publishBatchId + "  >>> " + adSetBatchIndex);
        try {
            AdSetLevel adSetLevel =(AdSetLevel)baseAdLevel;
            //1、获取发布批次
            PublishBatch publishBatch = publishBatchRdbService.findById(publishBatchId);
            if (checkBatch(publishBatchId, publishBatch)) return;
            //2、每次都取最新的 accessToken
            String accessToken = fbUserRdbService.getTokenByUserId(publishBatch.getUserId());

            Map<String, AdGroupLevel> adGroupLevelMap = JSON.parseObject(publishCacheMongoService.findCacheBlockByType(publishBatchId,
                    PublishCacheMongoService.ADGROUP), new TypeReference<HashMap<String,AdGroupLevel>>(){});
            List<String> adGroupBatchIndexsList = adSetLevel.getAdGroupBatchIndexs();
            logger.info("准备发送  adGroupBatchIndexsList >>>> " + adGroupBatchIndexsList);
            //3、判断AdSet发布成功与否
            //3-1、AdSet发布成功
            if (adSetLevel.getStatus() == PublishStatus.SUCCEED.getCode()) {
                Long adSetId = adSetLevel.getAdSetId();
                logger.info("publishAdGroups publishBatchId >>>> " + publishBatchId + "  >>> " + adSetBatchIndex + " ; adSetId >>> " + adSetId);
                //Adset成功则发布其下广告
                for (String adGroupIndex : adGroupBatchIndexsList) {
                    logger.debug("准备发送  adGroupIndex >>>> " + adGroupIndex);
                    AdGroupLevel adGroupLevel = adGroupLevelMap.get(adGroupIndex);
                    adGroupLevel.setAdSetId(adSetId);
                    adGroupLevel.setAdCampaignGroupId(adSetLevel.getCampaignGroupId());
                    String adCreativeBatchIndex = adGroupLevel.getAdCreativeBatchIndex();
                    AdCreativeLevel adCreativeLevel = JSON.parseObject(publishCacheMongoService.findCacheBlockByTypeAndKey(publishBatchId,
                            PublishCacheMongoService.CREATIVE,adCreativeBatchIndex),AdCreativeLevel.class);
                    if (adCreativeLevel != null && adCreativeLevel.getStatus() == PublishStatus.SUCCEED.getCode()) {
                        //3-1-1 创意成功
                        if(adGroupLevel.getStatus()==PublishStatus.WAIT_PUPLISH.getCode()||
                                adGroupLevel.getStatus()==PublishStatus.FAIL.getCode()){
                            try {
                                //3-1-2调用fb发布广告
                                adGroupLevel.setAdCreativeId(adCreativeLevel.getAdCreativeId());
                                adGroupLevel.setAccessToken(accessToken);
                                AdGroupParam adGroupParam = createAdGroupParam(adGroupIndex, adGroupLevel);
                                adGroupFBService.createAdGroup(adGroupParam);
                                publishCacheMongoService.updateStatus(adGroupLevel.getPublishBatchId(),adGroupLevel.getAdGroupKey(),
                                        PublishStatus.PUBLISHING.getCode(),PublishCacheMongoService.ADGROUP);
                                logger.info("发布成功adGroupIndex >>>> " + adGroupIndex);
                            } catch (Exception e) {
                                logger.error("发布或者保存数据库失败 adGroupIndex >>>> " + adGroupIndex, e);
                                adGroupLevel.setStatus(PublishStatus.FAIL.getCode());
                                savePublishResult(adGroupLevel, PublishResultType.adGroupFail, "{\"error\":{\"message\":\"adGroup has error," + e.getMessage() + "\"}}", AD_GROUP);
                            }
                        }
                    }
                    //3-1-2 创意失败
                    else {
                        adGroupLevel.setStatus(PublishStatus.FAIL.getCode());
                        //由于创意发布失败  广告发布失败 写入预发布表
                        savePublishResult(adGroupLevel, PublishResultType.adGroupFail,  ErrorCode.ERROR_20009,AD_GROUP);
                    }
                }
            }
            //3-2、失败
            else {
                logger.debug("adset发布失败，adgroup不用发布, adSetBatchIndex >>>>> " + adSetBatchIndex + " ; adGroupBatchIndexsList >>> " + adGroupBatchIndexsList);
                List<Long> adGroupLevelIds = new ArrayList<>();
                //3-2-1 修改状态并保存publish_result_record和publish_result_detail
                for (String adGroupIndex : adGroupBatchIndexsList) {
                    AdGroupLevel adGroupLevel = adGroupLevelMap.get(adGroupIndex);
                    adGroupLevelIds.add(adGroupLevel.getPreId());
                    adGroupLevel.setStatus(PublishStatus.FAIL.getCode());
                    savePublishResult(adGroupLevel, PublishResultType.adGroupFail, ErrorCode.ERROR_20010,AD_GROUP);
                }
                //由于广告组发布失败  广告发布失败 写入预发布表
            }
            logger.info("PublishAdGroupJob publishAdGroups 调用完成 ");
        }catch (Exception e){
            logger.error("创建批次adset 对应的 adgroup 出错 >>>> batchId：" +publishBatchId+"adSetBatchIndex: "+adSetBatchIndex+e.getMessage(),e );
        }
    }


    /**
     * 构造FB adGroupParam参数
     *
     * @param adGroupIndex
     * @param adGroupLevel
     * @return
     */
    private AdGroupParam createAdGroupParam(String adGroupIndex, AdGroupLevel adGroupLevel) {
        AdGroupParam adGroupParam = new AdGroupParam();
        adGroupParam.setAccessToken(adGroupLevel.getAccessToken());
        adGroupParam.setFbAccountId(Long.parseLong(adGroupLevel.getActAccountId().substring(4)));
        adGroupParam.setCampaignId(adGroupLevel.getAdCampaignGroupId());
        adGroupParam.setAdCreativeId(adGroupLevel.getAdCreativeId());
        adGroupParam.setCreative("{'creative_id':" + adGroupLevel.getAdCreativeId() + "}");
        adGroupParam.setName(adGroupLevel.getName());
        adGroupParam.setTrackingSpecs(com.alibaba.fastjson.JSONObject.parseObject(adGroupLevel.getTrackingSpecs().toString(),TrackingSpecs.class));
        adGroupParam.setStatus("ACTIVE");
        adGroupParam.setAdSetId(adGroupLevel.getAdSetId());
        adGroupParam.setApiPriorityCode(APIPriority.CreateAdGroups.getCode());

        adGroupParam.setRedownload(true);
        adGroupParam.setCallBack(true);
        adGroupParam.setResultClass(ReDownloadObject.class);

        //附加携带参数
        Map<String, String> equipment = new HashMap<>();
        adGroupParam.setApiCallbackTargetType(ApiCallBackTarget.AdPublish.AdGroup.getTargetType());
        equipment.put("publishBatchId", adGroupLevel.getPublishBatchId().toString());
        equipment.put("adGroupBatchIndex", adGroupIndex);
        equipment.put("adSetId", String.valueOf(adGroupLevel.getAdSetId()));
        equipment.put("campaignGroupId", String.valueOf(adGroupLevel.getAdCampaignGroupId()));
        equipment.put("creativeId", String.valueOf(adGroupLevel.getAdCreativeId()));
        adGroupParam.setEquipment(equipment);
        return adGroupParam;
    }

    @Override
    public void callback(Map<String, String> equipment, Object callBackObject) {
        ThreadPool.execute(()->{
            logger.info("PublishAdGroupJob publishAdGroupCallBack 回调开始 ");
            try {
                //1.通过equipment中的publishBatchId获取缓存中的AdPublishCache ，adGroupBatchIndex获取批次下的AdGroupLevel
                String publishBatchId = equipment.get("publishBatchId");
                String adGroupBatchIndex = equipment.get("adGroupBatchIndex");
                logger.info("回调 publishAdGroupCallBack publishBatchId >>>> " + publishBatchId);
                logger.info("回调 adGroupBatchIndex >>>> " + adGroupBatchIndex);

                //1.通过publishBatchId获取缓存中的AdPublishCache
                AdGroupLevel adGroupLevel = JSON.parseObject(publishCacheMongoService.findCacheBlockByTypeAndKey(Long.valueOf(publishBatchId),
                        PublishCacheMongoService.ADGROUP, adGroupBatchIndex),AdGroupLevel.class);
                adGroupLevel.setAdCampaignGroupId(Long.valueOf(equipment.get("campaignGroupId")));
                adGroupLevel.setAdSetId(Long.valueOf(equipment.get("adSetId")));
                adGroupLevel.setAdCreativeId(Long.valueOf(equipment.get("creativeId")));
                publishHandle(equipment, callBackObject, adGroupLevel);
            } catch (Exception e) {
                logger.error("publishAdGroupCallBack->->广告发布失败", e);
            }
            logger.debug("PublishAdGroupJob publishAdGroupCallBack 回调完成 ");
        });
    }

    @Override
    PublishResultDetailRdbService getPublishResultDetailRdbService() {
        return this.publishResultDetailRdbService;
    }

    @Override
    PublishResultRecordRdbService getPublishResultRecordRdbService() {
        return this.publishResultRecordRdbService;
    }

    //2.解析response，获取FB响应值
    private void publishHandle(Map<String, String> equipment, Object callBackObject, AdGroupLevel groupLevel) throws CheckedException {
        Long adGroupId = null;
        PublishStatus publishStatus;
        PublishResultType publishResultType;
        String exception = null;
        ReDownloadObject reDownLoad = null;
        if (callBackObject instanceof  ReDownloadObject) {
            reDownLoad = (ReDownloadObject)callBackObject;
            adGroupId = reDownLoad.getId();
            publishStatus = PublishStatus.SUCCEED;
            publishResultType = PublishResultType.adGroupSuccess;

        } else {
            //重试失败标记
            exception = callBackObject.toString();
            publishStatus = PublishStatus.FAIL;
            publishResultType = PublishResultType.adGroupFail;
        }
        savePublishResult(groupLevel, publishResultType, exception,AD_GROUP);
        equipment.put("adGroupId",adGroupId==null?null:String.valueOf(adGroupId));
        publishCacheMongoService.fillFbId(groupLevel.getPublishBatchId(),groupLevel.getAdGroupKey(),String.valueOf(adGroupId),PublishCacheMongoService.ADGROUP);
        publishCacheMongoService.updateStatus(groupLevel.getPublishBatchId(),groupLevel.getAdGroupKey(),publishStatus.getCode(),PublishCacheMongoService.ADGROUP);
        publishPostProcessorHandle.handle(new PublishPostResult(equipment,publishStatus,reDownLoad,groupLevel));
    }

}
